﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using HtmlAgilityPack;
using Telerik.WinControls;
using TrendingReader.Classes;
using CSharpVerbalExpressions;
using System.Web;

namespace TrendingReader.V2
{
    public partial class FrmGetStories : Telerik.WinControls.UI.RadForm
    {
        private TrendingReaderEntities _entities;

        public FrmGetStories()
        {
            InitializeComponent();
        }

        private void FrmGetStories_Load(object sender, EventArgs e)
        {
            _entities = new TrendingReaderEntities();
        }

        private void FrmGetStories_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (BgwGetAll.IsBusy)
            {
                RadMessageBox.Show("We are running, please wait for minutes.", "Opp!", MessageBoxButtons.OK, RadMessageIcon.Error);
                e.Cancel = true;
            }
        }

        private void BtnGetAll_Click(object sender, EventArgs e)
        {
            BgwGetAll.RunWorkerAsync();
        }

        private void BgwGetAll_DoWork(object sender, DoWorkEventArgs e)
        {
            BtnGetAll.Invoke((MethodInvoker)(() => BtnGetAll.Enabled = false));

            GetStories();

            GetChapters();

            GetChapterItems();
        }

        private void BgwGetAll_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (e.UserState == null)
                return;

            switch (e.UserState.ToString())
            {
                case "GetStories":
                    PgbGetStories.Value1 = e.ProgressPercentage;
                    break;
                case "GetChapters":
                    PgbGetChapters.Value1 = e.ProgressPercentage;
                    break;
                case "GetChapterItems":
                    PgbGetItems.Value1 = e.ProgressPercentage;
                    break;
            }
        }

        private void BgwGetAll_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            BtnGetAll.Invoke((MethodInvoker)(() => BtnGetAll.Enabled = true));
            LsbMessage.Invoke((MethodInvoker)(() => LsbMessage.Items.Clear()));
        }

        private void GetStories()
        {
            try
            {
                var countNewStories = 0;
                var countProcess = 0;
                var web = new HtmlWeb();

                LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = "Getting resource..."));
                LblGetStoriesStatus.Invoke((MethodInvoker)(() => LblGetStoriesStatus.Text = "Getting"));

                var doc = web.Load("http://truyentranhtuan.com/danh-sach-truyen");

                if (doc == null)
                {
                    LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = "Cannot getting resource."));
                    LblGetStoriesStatus.Invoke((MethodInvoker)(() => LblGetStoriesStatus.Text = "Error"));
                    return;
                }

                var storiesList = doc.DocumentNode.SelectNodes("//div[@class='manga-focus']//span[@class='manga']//a");

                if (storiesList.Count == 0)
                {
                    return;
                }

                var countStoriesInDb = _entities.Stories.Count();
                if (storiesList.Count == countStoriesInDb)
                {
                    LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = "We don't have any new Stories."));
                    LblGetStoriesStatus.Invoke((MethodInvoker)(() => LblGetStoriesStatus.Text = "Completed"));
                    PgbGetStories.Invoke((MethodInvoker)(() => PgbGetStories.Value1 = PgbGetStories.Maximum));
                    return;
                }

                // setting progress bar
                PgbGetStories.Invoke((MethodInvoker)(() => PgbGetStories.Value1 = 0));
                PgbGetStories.Invoke((MethodInvoker)(() => PgbGetStories.Maximum = storiesList.Count));

                foreach (var story in storiesList)
                {
                    countProcess++;
                    BgwGetAll.ReportProgress(countProcess, "GetStories");

                    if (story == null || string.IsNullOrEmpty(story.Attributes["href"].Value))
                        continue;

                    var storyName = HttpUtility.HtmlDecode(story.InnerText.Trim());
                    var storyLink = story.Attributes["href"].Value.ToLower().Trim();

                    CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("Getting {0}...", storyName));

                    // check exist story
                    if (CheckExistStoryByLink(storyLink))
                    {
                        CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("\"{0}\" is existed.", storyName));
                        continue;
                    }

                    // save to database
                    var newStory = new Stories();
                    newStory.Id = Guid.NewGuid();
                    newStory.Name = storyName;
                    newStory.Link = storyLink;
                    newStory.UpdateDate = DateTime.Now;
                    newStory.RunDate = DateTime.Now;
                    _entities.Stories.Add(newStory);
                    countNewStories++;

                    CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("\"{0}\" is saved.", storyName));
                    LblStoriesAddedCount.Invoke((MethodInvoker)(() => LblStoriesAddedCount.Text = countNewStories.ToString()));
                }

                if (countNewStories > 0)
                {
                    _entities.SaveChanges();
                    LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = string.Format("{0} new stories are added.", countNewStories)));                    
                }
                else
                {
                    LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = "Your stories list is newest."));
                    PgbGetStories.Invoke((MethodInvoker)(() => PgbGetStories.Value1 = PgbGetStories.Maximum));
                }

                LblGetStoriesStatus.Invoke((MethodInvoker)(() => LblGetStoriesStatus.Text = "Completed"));

            }
            catch (Exception ex)
            {
                RadMessageBox.Show(ex.Message, "Opp!", MessageBoxButtons.OK, RadMessageIcon.Error);
            }
        }

        public void GetChapters()
        {
            try
            {
                var countNewChapters = 0;
                var countProcess = 0;
                var hasChange = false;
                var web = new HtmlWeb();
                HtmlAgilityPack.HtmlDocument doc;

                LblGetChaptersStatus.Invoke((MethodInvoker)(() => LblGetChaptersStatus.Text = "Getting"));

                // get stories from database
                var storiesList = _entities.Stories.OrderBy(s => s.Name).ToList();
                if (storiesList.Count == 0)
                {
                    LblMessage.Invoke((MethodInvoker)(()=> LblMessage.Text = "Doesnot exist any stories."));
                    return;
                }
                
                // setting progress bar
                countProcess = 0;
                PgbGetChapters.Invoke((MethodInvoker)(() => PgbGetChapters.Value1 = 0));
                PgbGetChapters.Invoke((MethodInvoker)(() => PgbGetChapters.Maximum = storiesList.Count));
                
                // foreach story to get chapters
                foreach (var story in storiesList)
                {
                    // change process percent
                    countProcess++;
                    BgwGetAll.ReportProgress(countProcess, "GetChapters");

                    if (story == null || string.IsNullOrEmpty(story.Link))
                        continue;

                    hasChange = false;

                    LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = string.Format("Getting {0}...", story.Name)));

                    // get link
                    try
                    {
                        doc = web.Load(story.Link);
                    }
                    catch (Exception)
                    {
                        continue;
                    }

                    if (doc == null)
                    {
                        LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = string.Format("Cannot load Resource.")));
                        return;
                    }

                    // clear log
                    LsbMessage.Invoke((MethodInvoker)(() => LsbMessage.Items.Clear()));

                    var chaptersList = doc.DocumentNode.SelectNodes("//div[@id='main-content']//div[@id='manga-chapter']//span[@class='chapter-name']//a");

                    if (chaptersList == null || chaptersList.Count == 0)
                    {
                        LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = string.Format("{0} doesnot exist any chapters.", story.Name)));
                        CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("{0} doesnot exist any chapters.", story.Name));
                        continue;
                    }

                    var countChaptersInDb = _entities.Chapters.Where(c => c.StoryId == story.Id).Count();

                    if (chaptersList.Count == countChaptersInDb)
                    {
                        LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = string.Format("We don't have any new for {0}.", story.Name)));
                        CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("We don't have any new for {0}.", story.Name));
                        continue;
                    }

                    var orderIndex = 0;
                    for (int i = chaptersList.Count - 1; i >= 0; i--)
                    {
                        orderIndex++;
                        var chapter = chaptersList[i];
                        if (string.IsNullOrEmpty(chapter.Attributes["href"].Value.ToLower().Trim()))
                            continue;

                        var chapterName = HttpUtility.HtmlDecode(chapter.InnerText.Trim());
                        var chapterLink = chapter.Attributes["href"].Value.ToLower().Trim();

                        CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("Getting \"{0}\"...", chapterName));

                        // check exist chapter
                        if (CheckExistChapterByLink(chapterLink))
                        {
                            CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("\"{0}\" is existed.", chapterName));
                            continue;
                        }

                        // save data to database
                        var newChapter = new Chapters();
                        newChapter.Id = Guid.NewGuid();
                        newChapter.StoryId = story.Id;
                        newChapter.Name = chapterName;
                        newChapter.UpdateDate = DateTime.Now;
                        newChapter.RunDate = DateTime.Now;
                        newChapter.Link = chapterLink;
                        newChapter.OrderIndex = orderIndex;
                        _entities.Chapters.Add(newChapter);
                        countNewChapters++;
                        hasChange = true;

                        CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("\"{0}\" is saved.", chapterName));
                        LblChaptersAddedCount.Invoke((MethodInvoker)(() => LblChaptersAddedCount.Text = countNewChapters.ToString()));
                    }

                    if (hasChange)
                    {
                        try
                        {
                            story.UpdateDate = DateTime.Now;
                            _entities.SaveChanges();
                        }
                        catch (Exception)
                        {
                        }
                    }
                }

                var message = countNewChapters > 0 ? string.Format("{0} new chapters are added.", countNewChapters) : "Your chapters list is newest.";
                LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = message));

                LblGetChaptersStatus.Invoke((MethodInvoker)(() => LblGetChaptersStatus.Text = "Completed"));
                if (countNewChapters == 0)
                {
                    PgbGetChapters.Invoke((MethodInvoker)(() => PgbGetChapters.Value1 = PgbGetChapters.Maximum));
                }
            }
            catch (Exception ex)
            {
                RadMessageBox.Show(ex.Message, "Opp!", MessageBoxButtons.OK, RadMessageIcon.Error);
                LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = ex.Message));
                return;
            }
        }

        public void GetChapterItems()
        {
            try
            {
                bool hasChange;
                var countNewItems = 0;
                var web = new HtmlWeb();
                HtmlAgilityPack.HtmlDocument doc;

                LblGetItemsStatus.Invoke((MethodInvoker)(() => LblGetItemsStatus.Text = "Getting"));

                // get chapters from database
                var chaptersList = _entities.Chapters.OrderBy(s => s.UpdateDate).ToList();

                if (chaptersList.Count == 0)
                {
                    LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = "Doesnot exist any chapters."));
                    LblGetItemsStatus.Invoke((MethodInvoker)(() => LblGetItemsStatus.Text = "Completed"));
                    PgbGetItems.Invoke((MethodInvoker)(() => PgbGetItems.Value1 = PgbGetItems.Maximum));
                    return;
                }

                // setting progress bar
                var countProcess = 0;
                PgbGetItems.Invoke((MethodInvoker)(() => PgbGetItems.Value1 = 0));
                PgbGetItems.Invoke((MethodInvoker)(() => PgbGetItems.Maximum = chaptersList.Count));

                // foreach story to get chapters
                foreach (var chapter in chaptersList)
                {
                    countProcess++;
                    BgwGetAll.ReportProgress(countProcess, "GetChapterItems");

                    hasChange = false;

                    if (chapter == null || string.IsNullOrEmpty(chapter.Link))
                        continue;

                    LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = string.Format("Getting {0}...", chapter.Name)));

                    // get link
                    try
                    {
                        doc = web.Load(chapter.Link);
                    }
                    catch (Exception)
                    {
                        continue;
                    }

                    if (doc == null)
                    {
                        LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = "Cannot load Resource."));
                        continue;
                    }

                    // clear log
                    LsbMessage.Invoke((MethodInvoker)(() => LsbMessage.Items.Clear()));

                    var htmlText = doc.DocumentNode.InnerHtml;

                    var isGoogleLink = true;
                    var strLinks = string.Empty;
                    string[] linksList = { };

                    var verbEx = new VerbalExpressions()
                        .Then("var slides_page_url_path = [")
                        .Anything()
                        .Then("];");

                    strLinks = verbEx.Capture(htmlText, "2");

                    if (string.IsNullOrEmpty(strLinks))
                    {
                        verbEx = new VerbalExpressions()
                        .Then("var slides_page_path = [")
                        .Anything()
                        .Then("];");
                        isGoogleLink = false;
                        strLinks = verbEx.Capture(htmlText, "2");
                    }

                    if (string.IsNullOrEmpty(strLinks))
                    {
                        LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = string.Format("{0} doesnot exist content.", chapter.Name)));
                        CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("{0} doesnot exist content.", chapter.Name));
                        continue;
                    }

                    if (strLinks.Contains(","))
                    {
                        linksList = strLinks.Split(',');
                    }
                    else if (strLinks.Contains("|"))
                    {
                        linksList = strLinks.Split('|');
                    }

                    var itemsList = new List<string>();
                    foreach (var link in linksList)
                    {
                        itemsList.Add(link.Trim().Replace("\"", ""));
                    }

                    var countItemsInDb = _entities.Items.Where(i => i.ChapterId == chapter.Id).Count();

                    if (itemsList.Count == 0 || itemsList.Count == countItemsInDb)
                    {
                        LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = string.Format("We don't have new content for {0}.", chapter.Name)));
                        CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("We don't have new content for {0}.", chapter.Name));
                        continue;
                    }

                    var orderIndex = 0;

                    if (!isGoogleLink)
                        itemsList.OrderBy(i => i);

                    foreach (var item in itemsList)
                    {
                        orderIndex++;

                        if (string.IsNullOrEmpty(item))
                            continue;

                        var imgName = CommonFunctions.GenerateFileName(chapter.Name, orderIndex.ToString());
                        if (CheckExistChapterItemByLink(item))
                        {
                            CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("\"{0}\" is existed.", imgName));
                            continue;
                        }

                        //// download image
                        //var downloadFolder = CommonFunctions.GetDownloadFolder(chapter.Stories.Name, chapter.Name);

                        //var imgPath = downloadFolder + imgName + ".jpg";
                        //using (var webClient = new WebClient())
                        //{
                        //    webClient.DownloadFileAsync(new Uri(item), imgPath);
                        //}

                        // save data to database
                        var newItem = new Items();
                        newItem.Id = Guid.NewGuid();
                        newItem.ChapterId = chapter.Id;
                        newItem.Name = imgName;
                        newItem.Link = item;
                        newItem.UpdateDate = DateTime.Now;
                        newItem.RunDate = DateTime.Now;
                        newItem.OrderIndex = orderIndex;
                        _entities.Items.Add(newItem);
                        countNewItems++;
                        hasChange = true;

                        CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("\"{0}\" is saved.", imgName));
                        LblItemsAddedCount.Invoke((MethodInvoker)(() => LblItemsAddedCount.Text = countNewItems.ToString()));
                    }

                    if (hasChange)
                    {
                        try
                        {
                            chapter.UpdateDate = DateTime.Now;
                            chapter.Stories.UpdateDate = DateTime.Now;
                            _entities.SaveChanges();
                        }
                        catch (Exception)
                        {
                            continue;
                        }
                    }
                }

                LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = "All items are added."));
                LblGetItemsStatus.Invoke((MethodInvoker)(() => LblGetItemsStatus.Text = "Completed"));
                PgbGetItems.Invoke((MethodInvoker)(() => PgbGetItems.Value1 = PgbGetItems.Maximum));
            }
            catch (Exception ex)
            {
                RadMessageBox.Show(ex.Message, "Opp!", MessageBoxButtons.OK, RadMessageIcon.Error);
                LblMessage.Invoke((MethodInvoker)(() => LblMessage.Text = ex.Message));
                return;
            }
        }

        private bool CheckExistStoryByLink(string link)
        {
            var blnExisted = false;

            var story = _entities.Stories.FirstOrDefault(s => s.Link.ToLower().Trim() == link.ToLower().Trim());

            blnExisted = (story != null);

            return blnExisted;
        }

        private bool CheckExistChapterByLink(string link)
        {
            var blnExisted = false;

            var story = _entities.Chapters.FirstOrDefault(s => s.Link.ToLower().Trim() == link.ToLower().Trim());

            blnExisted = (story != null);

            return blnExisted;
        }

        private bool CheckExistChapterItemByLink(string link)
        {
            var blnExisted = false;

            var item = _entities.Items.FirstOrDefault(s => s.Link.ToLower().Trim() == link.ToLower().Trim());

            blnExisted = (item != null);

            return blnExisted;
        }

    }
}
